package zhuzx.solution.dbt;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.zhuzx.util.*;

import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;

/**
 * @author zhuzx 2022/7/19
 */
@Slf4j
public class DbtPermissionSQLGenerator {
	
	/** 直接取开发库的不太保险，构造时可以手动填入初始值，比如取测试和生产数据库目前的最大id，避免和其它环境冲突。*/
	private static final AtomicLong currentPermissionsId = new AtomicLong();
	private static String dbName = null;

	public static void main(String[] args) {
//		String url = "jdbc:mysql://10.88.0.18:3306/userportaldb?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull";
//		String user = "userportal", password = "userportal123";
		String url = "jdbc:mysql://10.88.0.18:3306/proddb?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull";
		String user = "prod", password = "prod123";
		Connection conn = JdbcUtil.getDbConn(url, user, password);
		if (currentPermissionsId.get() == 0) {//默认无参构造方法new出来就是0
			String id = JdbcUtil.executeQuery("select max(id) as id from permissions", conn).get(0).get("id");
			log.info("使用库中已有的最大id值{}加1作为起始id", id);
			currentPermissionsId.set(Long.parseLong(id));
		}
		try {
			dbName = conn.getCatalog();
			conn.close();
		} catch (SQLException e) {throw new RuntimeException(e);}
//		String swaggerUrl = "http://localhost:8184/UserPortalServ/";
		String swaggerUrl = "http://localhost:8085/ProdMgntServ/";
		String[] targets = {
				"commonDict", "releaseRefChannel", "releaseTask"
		};
		generate(GeneralUtil.concatStringWithUnduplicatedConnector(swaggerUrl, "/", "v2/api-docs"),
				Arrays.asList(targets));
	}
/**
paths下的内容，是一个个的这样的对象：
"/publishChangeRecord/getByIdBatch":{
	"get":{
		"tags":[
			"产品动态"
		],
		"summary":"产品动态-根据id批量获取多条",
		"description":"返回的数据按changeTime倒序排列",
		"operationId":"getByIdBatchUsingGET",
		"produces":[""],
		"parameters":[],
		"responses":{},
		"deprecated":false
	}
}
*/

	private static void generate(String swaggerUrl, List<String> targets) {
		//一个接口名对应paths中的一个键，这个接口名（路径）对应的值也是个map，里面可能最多有7个键值对，对应http的7种method。
		Map<String, Object> apis = JSON.parseObject(HttpClientUtil.get(swaggerUrl, null))
				.getJSONObject("paths");
		//将那些有重复method（比如既有get又有post）的给剔除掉（以前的老接口没有标注get或者post的）
		//按路径排序，实测getJSONObject("paths")拿到的键值对是完全乱序的，同一个Controller中的到处分散。
		apis = apis.entrySet().stream().filter(item -> ((JSONObject) item.getValue()).size() == 1)
				.sorted(Map.Entry.comparingByKey())
				.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (u, v) -> {
						throw new IllegalStateException();
				}, LinkedHashMap::new));
		if (!GeneralUtil.isEmpty(targets)) {
			List<String> targetPaths = targets.stream().map(item -> {
				String closeWord = item.startsWith("/") ? item : "/" + item;
				closeWord = closeWord.endsWith("/") ? closeWord : closeWord + "/";
				return closeWord;
			}).collect(Collectors.toList());
			apis = apis.entrySet().stream().filter(item -> {
				String key = item.getKey();
				key = key.substring(0, key.indexOf('/', 1) + 1);
				return targetPaths.contains(key);
			}).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
					LambdaConstants.neverHappenMerger(), LinkedHashMap::new));
		}
		List<Map<String, String>> permissionsData = apis.entrySet().stream()
				.map(DbtPermissionSQLGenerator::generateByApi).filter(Objects::nonNull)
				.collect(Collectors.toList());
		addReadWrite(permissionsData);
		String permTable = dbName + ".permissions";
		FileOperationUtil.writeToFile((JdbcUtil.createBatchInsertSql(permTable, permissionsData) + ";")
				.getBytes(StandardCharsets.UTF_8), permTable + ".sql", true);
	}
	
	private static Map<String,String> generateByApi(Map.Entry<String,Object> api) {
		String url = api.getKey();
		JSONObject types = (JSONObject) api.getValue();
		//同一个接口可能有get、post等多个重复的，不管前面有没有对这种做剔除，这里取第一个方法就行。
		JSONObject info = (JSONObject) types.entrySet().iterator().next().getValue();
		String tag = info.getJSONArray("tags").getString(0);
		String name = info.getString("summary");
		//若Controller类和方法上面没标注中文，说明没有打swagger注解，不给生成。
		if (MicroUtil.findFirst(tag, UnifiedConstants.REGEX_CHINESE_CHAR) == -1
				|| MicroUtil.findFirst(name, UnifiedConstants.REGEX_CHINESE_CHAR) == -1) {
			return null;
		}
		if (!name.startsWith(tag) && !name.contains("-")) {
			name = tag + "-" + name;
		}
		String methodName = MicroUtil.subBehind(url, '/');
		String[] readOnlyKeywords = {"select", "query", "list", "get", "find", "aggregate"};
		String regexReadOnly = Arrays.stream(readOnlyKeywords).map(item ->
				"((?i)" + item.charAt(0) + ")" + item.substring(1)
		).collect(Collectors.joining("|"));
		String type = methodName.matches(".*(" + regexReadOnly + ").*") ? "read" : "write";
		//去掉第一个斜杠和后面的方法名，以及最后一个斜杠。
		String module = url.substring(1);
		module = module.substring(0, module.length() - (methodName.length() + 1));
		String permission = module + ":" + type + ":" + methodName;
		Map<String,String> row = new LinkedHashMap<>();
		row.put("id", String.valueOf(currentPermissionsId.incrementAndGet()));
		row.put("name", name);
		row.put("type", "meta");
		row.put("url", url);
		row.put("permission", permission);
		row.put("priority", "1");
		row.put("filter_type", "perms");
		return row;
	}

	private static void addReadWrite(List<Map<String, String>> permissionDataList) {
		Set<Map<String,String>> readWrites = new LinkedHashSet<>();
		permissionDataList.forEach(item -> readWrites.addAll(generateReadWrite(item)));
		readWrites.forEach(item -> item.put("id", String.valueOf(currentPermissionsId.incrementAndGet())));
		permissionDataList.addAll(readWrites);
	}

	private static List<Map<String,String>> generateReadWrite(Map<String, String> metaPerm) {
		List<Map<String,String>> list = new ArrayList<>(2);
		String name = MicroUtil.subBefore(metaPerm.get("name"), '-');
		String module = MicroUtil.subBefore(metaPerm.get("permission"), ':');
		Map<String,String> row = new LinkedHashMap<>();
		row.put("id", "");//占位保持Map中的顺序为第一位
		row.put("name", name + "（读）");
		row.put("type", "uperm");
		row.put("url", null);
		row.put("permission", module + ":read:*");
		row.put("priority", "1");
		row.put("filter_type", "perms");
		list.add(row);
		row = new LinkedHashMap<>();
		row.put("id", "");
		row.put("name", name + "（读写）");
		row.put("type", "uperm");
		row.put("url", null);
		row.put("permission", module + ":*");
		row.put("priority", "1");
		row.put("filter_type", "perms");
		list.add(row);
		return list;
	}
	
}
